/*-------------------------------------------------------------------------
 *
 * plpgsql.h        - Definitions for the PL/pgSQL
 *              procedural language
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      src/pl/plpgsql/src/plpgsql.h
 *
 *-------------------------------------------------------------------------
 */

#ifndef PLPGSQL_H
#define PLPGSQL_H

#include "access/xact.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
#include "executor/spi.h"

/**********************************************************************
 * Definitions
 **********************************************************************/

/* define our text domain for translations */
#undef TEXTDOMAIN
#define TEXTDOMAIN PG_TEXTDOMAIN("plpgsql")

#undef _
#define _(x) dgettext(TEXTDOMAIN, x)

/*
 * Compiler's namespace item types
 */
typedef enum PLpgSQL_nsitem_type
{
    PLPGSQL_NSTYPE_LABEL,
    PLPGSQL_NSTYPE_VAR,
    PLPGSQL_NSTYPE_ROW,
    PLPGSQL_NSTYPE_REC
} PLpgSQL_nsitem_type;

/*
 * A PLPGSQL_NSTYPE_LABEL stack entry must be one of these types
 */
typedef enum PLpgSQL_label_type
{
    PLPGSQL_LABEL_BLOCK,        /* DECLARE/BEGIN block */
    PLPGSQL_LABEL_LOOP,            /* looping construct */
    PLPGSQL_LABEL_OTHER            /* anything else */
} PLpgSQL_label_type;

/*
 * Datum array node types
 */
typedef enum PLpgSQL_datum_type
{
    PLPGSQL_DTYPE_VAR,
    PLPGSQL_DTYPE_ROW,
    PLPGSQL_DTYPE_REC,
    PLPGSQL_DTYPE_RECFIELD,
    PLPGSQL_DTYPE_ARRAYELEM,
    PLPGSQL_DTYPE_EXPR
} PLpgSQL_datum_type;

/*
 * Variants distinguished in PLpgSQL_type structs
 */
typedef enum PLpgSQL_type_type
{
    PLPGSQL_TTYPE_SCALAR,        /* scalar types and domains */
    PLPGSQL_TTYPE_ROW,            /* composite types */
    PLPGSQL_TTYPE_REC,            /* RECORD pseudotype */
    PLPGSQL_TTYPE_PSEUDO        /* other pseudotypes */
} PLpgSQL_type_type;

/*
 * Execution tree node types
 */
typedef enum PLpgSQL_stmt_type
{
    PLPGSQL_STMT_BLOCK,
    PLPGSQL_STMT_ASSIGN,
    PLPGSQL_STMT_IF,
    PLPGSQL_STMT_CASE,
    PLPGSQL_STMT_LOOP,
    PLPGSQL_STMT_WHILE,
    PLPGSQL_STMT_FORI,
    PLPGSQL_STMT_FORS,
    PLPGSQL_STMT_FORC,
    PLPGSQL_STMT_FOREACH_A,
    PLPGSQL_STMT_EXIT,
    PLPGSQL_STMT_RETURN,
    PLPGSQL_STMT_RETURN_NEXT,
    PLPGSQL_STMT_RETURN_QUERY,
    PLPGSQL_STMT_RAISE,
    PLPGSQL_STMT_ASSERT,
    PLPGSQL_STMT_EXECSQL,
    PLPGSQL_STMT_DYNEXECUTE,
    PLPGSQL_STMT_DYNFORS,
    PLPGSQL_STMT_GETDIAG,
    PLPGSQL_STMT_OPEN,
    PLPGSQL_STMT_FETCH,
    PLPGSQL_STMT_CLOSE,
    PLPGSQL_STMT_PERFORM
} PLpgSQL_stmt_type;

/*
 * Execution node return codes
 */
enum
{
    PLPGSQL_RC_OK,
    PLPGSQL_RC_EXIT,
    PLPGSQL_RC_RETURN,
    PLPGSQL_RC_CONTINUE
};

/*
 * GET DIAGNOSTICS information items
 */
typedef enum PLpgSQL_getdiag_kind
{
    PLPGSQL_GETDIAG_ROW_COUNT,
    PLPGSQL_GETDIAG_RESULT_OID,
    PLPGSQL_GETDIAG_CONTEXT,
    PLPGSQL_GETDIAG_ERROR_CONTEXT,
    PLPGSQL_GETDIAG_ERROR_DETAIL,
    PLPGSQL_GETDIAG_ERROR_HINT,
    PLPGSQL_GETDIAG_RETURNED_SQLSTATE,
    PLPGSQL_GETDIAG_COLUMN_NAME,
    PLPGSQL_GETDIAG_CONSTRAINT_NAME,
    PLPGSQL_GETDIAG_DATATYPE_NAME,
    PLPGSQL_GETDIAG_MESSAGE_TEXT,
    PLPGSQL_GETDIAG_TABLE_NAME,
    PLPGSQL_GETDIAG_SCHEMA_NAME
} PLpgSQL_getdiag_kind;

/*
 * RAISE statement options
 */
typedef enum PLpgSQL_raise_option_type
{
    PLPGSQL_RAISEOPTION_ERRCODE,
    PLPGSQL_RAISEOPTION_MESSAGE,
    PLPGSQL_RAISEOPTION_DETAIL,
    PLPGSQL_RAISEOPTION_HINT,
    PLPGSQL_RAISEOPTION_COLUMN,
    PLPGSQL_RAISEOPTION_CONSTRAINT,
    PLPGSQL_RAISEOPTION_DATATYPE,
    PLPGSQL_RAISEOPTION_TABLE,
    PLPGSQL_RAISEOPTION_SCHEMA
} PLpgSQL_raise_option_type;

/*
 * Behavioral modes for plpgsql variable resolution
 */
typedef enum PLpgSQL_resolve_option
{
    PLPGSQL_RESOLVE_ERROR,        /* throw error if ambiguous */
    PLPGSQL_RESOLVE_VARIABLE,    /* prefer plpgsql var to table column */
    PLPGSQL_RESOLVE_COLUMN        /* prefer table column to plpgsql var */
} PLpgSQL_resolve_option;


/**********************************************************************
 * Node and structure definitions
 **********************************************************************/

/*
 * Postgres data type
 */
typedef struct PLpgSQL_type
{
    char       *typname;        /* (simple) name of the type */
    Oid            typoid;            /* OID of the data type */
    PLpgSQL_type_type ttype;    /* PLPGSQL_TTYPE_ code */
    int16        typlen;            /* stuff copied from its pg_type entry */
    bool        typbyval;
    char        typtype;
    Oid            typrelid;
    Oid            collation;        /* from pg_type, but can be overridden */
    bool        typisarray;        /* is "true" array, or domain over one */
    int32        atttypmod;        /* typmod (taken from someplace else) */
} PLpgSQL_type;

/*
 * Generic datum array item
 *
 * PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
 * PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem
 */
typedef struct PLpgSQL_datum
{
    PLpgSQL_datum_type dtype;
    int            dno;
} PLpgSQL_datum;

/*
 * Scalar or composite variable
 *
 * The variants PLpgSQL_var, PLpgSQL_row, and PLpgSQL_rec share these
 * fields
 */
typedef struct PLpgSQL_variable
{
    PLpgSQL_datum_type dtype;
    int            dno;
    char       *refname;
    int            lineno;
} PLpgSQL_variable;

/*
 * SQL Query to plan and execute
 */
typedef struct PLpgSQL_expr
{
    PLpgSQL_datum_type dtype;
    int            dno;
    char       *query;
    SPIPlanPtr    plan;
    Bitmapset  *paramnos;        /* all dnos referenced by this query */
    int            rwparam;        /* dno of read/write param, or -1 if none */

    /* function containing this expr (not set until we first parse query) */
    struct PLpgSQL_function *func;

    /* namespace chain visible to this expr */
    struct PLpgSQL_nsitem *ns;

    /* fields for "simple expression" fast-path execution: */
    Expr       *expr_simple_expr;    /* NULL means not a simple expr */
    int            expr_simple_generation; /* plancache generation we checked */
    Oid            expr_simple_type;    /* result type Oid, if simple */
    int32        expr_simple_typmod; /* result typmod, if simple */

    /*
     * if expr is simple AND prepared in current transaction,
     * expr_simple_state and expr_simple_in_use are valid. Test validity by
     * seeing if expr_simple_lxid matches current LXID.  (If not,
     * expr_simple_state probably points at garbage!)
     */
    ExprState  *expr_simple_state;    /* eval tree for expr_simple_expr */
    bool        expr_simple_in_use; /* true if eval tree is active */
    LocalTransactionId expr_simple_lxid;
} PLpgSQL_expr;

/*
 * Scalar variable
 */
typedef struct PLpgSQL_var
{
    PLpgSQL_datum_type dtype;
    int            dno;
    char       *refname;
    int            lineno;

    PLpgSQL_type *datatype;
    int            isconst;
    int            notnull;
    PLpgSQL_expr *default_val;
    PLpgSQL_expr *cursor_explicit_expr;
    int            cursor_explicit_argrow;
    int            cursor_options;

    Datum        value;
    bool        isnull;
    bool        freeval;
} PLpgSQL_var;

/*
 * Row variable
 */
typedef struct PLpgSQL_row
{
    PLpgSQL_datum_type dtype;
    int            dno;
    char       *refname;
    int            lineno;

    /* Note: TupleDesc is only set up for named rowtypes, else it is NULL. */
    TupleDesc    rowtupdesc;

    /*
     * Note: if the underlying rowtype contains a dropped column, the
     * corresponding fieldnames[] entry will be NULL, and there is no
     * corresponding var (varnos[] will be -1).
     */
    int            nfields;
    char      **fieldnames;
    int           *varnos;
} PLpgSQL_row;

/*
 * Record variable (non-fixed structure)
 */
typedef struct PLpgSQL_rec
{
    PLpgSQL_datum_type dtype;
    int            dno;
    char       *refname;
    int            lineno;

    HeapTuple    tup;
    TupleDesc    tupdesc;
    bool        freetup;
    bool        freetupdesc;
} PLpgSQL_rec;

/*
 * Field in record
 */
typedef struct PLpgSQL_recfield
{
    PLpgSQL_datum_type dtype;
    int            dno;
    char       *fieldname;
    int            recparentno;    /* dno of parent record */
} PLpgSQL_recfield;

/*
 * Element of array variable
 */
typedef struct PLpgSQL_arrayelem
{
    PLpgSQL_datum_type dtype;
    int            dno;
    PLpgSQL_expr *subscript;
    int            arrayparentno;    /* dno of parent array variable */

    /* Remaining fields are cached info about the array variable's type */
    Oid            parenttypoid;    /* type of array variable; 0 if not yet set */
    int32        parenttypmod;    /* typmod of array variable */
    Oid            arraytypoid;    /* OID of actual array type */
    int32        arraytypmod;    /* typmod of array (and its elements too) */
    int16        arraytyplen;    /* typlen of array type */
    Oid            elemtypoid;        /* OID of array element type */
    int16        elemtyplen;        /* typlen of element type */
    bool        elemtypbyval;    /* element type is pass-by-value? */
    char        elemtypalign;    /* typalign of element type */
} PLpgSQL_arrayelem;

/*
 * Item in the compilers namespace tree
 */
typedef struct PLpgSQL_nsitem
{
    PLpgSQL_nsitem_type itemtype;

    /*
     * For labels, itemno is a value of enum PLpgSQL_label_type. For other
     * itemtypes, itemno is the associated PLpgSQL_datum's dno.
     */
    int            itemno;
    struct PLpgSQL_nsitem *prev;
    char        name[FLEXIBLE_ARRAY_MEMBER];    /* nul-terminated string */
} PLpgSQL_nsitem;

/*
 * Generic execution node
 */
typedef struct PLpgSQL_stmt
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
} PLpgSQL_stmt;

/*
 * One EXCEPTION condition name
 */
typedef struct PLpgSQL_condition
{
    int            sqlerrstate;    /* SQLSTATE code */
    char       *condname;        /* condition name (for debugging) */
    struct PLpgSQL_condition *next;
} PLpgSQL_condition;

/*
 * EXCEPTION block
 */
typedef struct PLpgSQL_exception_block
{
    int            sqlstate_varno;
    int            sqlerrm_varno;
    List       *exc_list;        /* List of WHEN clauses */
} PLpgSQL_exception_block;

/*
 * One EXCEPTION ... WHEN clause
 */
typedef struct PLpgSQL_exception
{
    int            lineno;
    PLpgSQL_condition *conditions;
    List       *action;            /* List of statements */
} PLpgSQL_exception;

/*
 * Block of statements
 */
typedef struct PLpgSQL_stmt_block
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    List       *body;            /* List of statements */
    int            n_initvars;
    int           *initvarnos;
    PLpgSQL_exception_block *exceptions;
} PLpgSQL_stmt_block;

/*
 * Assign statement
 */
typedef struct PLpgSQL_stmt_assign
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    int            varno;
    PLpgSQL_expr *expr;
} PLpgSQL_stmt_assign;

/*
 * PERFORM statement
 */
typedef struct PLpgSQL_stmt_perform
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *expr;
} PLpgSQL_stmt_perform;

/*
 * GET DIAGNOSTICS item
 */
typedef struct PLpgSQL_diag_item
{
    PLpgSQL_getdiag_kind kind;    /* id for diagnostic value desired */
    int            target;            /* where to assign it */
} PLpgSQL_diag_item;

/*
 * GET DIAGNOSTICS statement
 */
typedef struct PLpgSQL_stmt_getdiag
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    bool        is_stacked;        /* STACKED or CURRENT diagnostics area? */
    List       *diag_items;        /* List of PLpgSQL_diag_item */
} PLpgSQL_stmt_getdiag;

/*
 * IF statement
 */
typedef struct PLpgSQL_stmt_if
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *cond;            /* boolean expression for THEN */
    List       *then_body;        /* List of statements */
    List       *elsif_list;        /* List of PLpgSQL_if_elsif structs */
    List       *else_body;        /* List of statements */
} PLpgSQL_stmt_if;

/*
 * one ELSIF arm of IF statement
 */
typedef struct PLpgSQL_if_elsif
{
    int            lineno;
    PLpgSQL_expr *cond;            /* boolean expression for this case */
    List       *stmts;            /* List of statements */
} PLpgSQL_if_elsif;

/*
 * CASE statement
 */
typedef struct PLpgSQL_stmt_case
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *t_expr;        /* test expression, or NULL if none */
    int            t_varno;        /* var to store test expression value into */
    List       *case_when_list; /* List of PLpgSQL_case_when structs */
    bool        have_else;        /* flag needed because list could be empty */
    List       *else_stmts;        /* List of statements */
} PLpgSQL_stmt_case;

/*
 * one arm of CASE statement
 */
typedef struct PLpgSQL_case_when
{
    int            lineno;
    PLpgSQL_expr *expr;            /* boolean expression for this case */
    List       *stmts;            /* List of statements */
} PLpgSQL_case_when;

/*
 * Unconditional LOOP statement
 */
typedef struct PLpgSQL_stmt_loop
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    List       *body;            /* List of statements */
} PLpgSQL_stmt_loop;

/*
 * WHILE cond LOOP statement
 */
typedef struct PLpgSQL_stmt_while
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    PLpgSQL_expr *cond;
    List       *body;            /* List of statements */
} PLpgSQL_stmt_while;

/*
 * FOR statement with integer loopvar
 */
typedef struct PLpgSQL_stmt_fori
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    PLpgSQL_var *var;
    PLpgSQL_expr *lower;
    PLpgSQL_expr *upper;
    PLpgSQL_expr *step;            /* NULL means default (ie, BY 1) */
    int            reverse;
    List       *body;            /* List of statements */
} PLpgSQL_stmt_fori;

/*
 * PLpgSQL_stmt_forq represents a FOR statement running over a SQL query.
 * It is the common supertype of PLpgSQL_stmt_fors, PLpgSQL_stmt_forc
 * and PLpgSQL_dynfors.
 */
typedef struct PLpgSQL_stmt_forq
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    PLpgSQL_rec *rec;
    PLpgSQL_row *row;
    List       *body;            /* List of statements */
} PLpgSQL_stmt_forq;

/*
 * FOR statement running over SELECT
 */
typedef struct PLpgSQL_stmt_fors
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    PLpgSQL_rec *rec;
    PLpgSQL_row *row;
    List       *body;            /* List of statements */
    /* end of fields that must match PLpgSQL_stmt_forq */
    PLpgSQL_expr *query;
} PLpgSQL_stmt_fors;

/*
 * FOR statement running over cursor
 */
typedef struct PLpgSQL_stmt_forc
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    PLpgSQL_rec *rec;
    PLpgSQL_row *row;
    List       *body;            /* List of statements */
    /* end of fields that must match PLpgSQL_stmt_forq */
    int            curvar;
    PLpgSQL_expr *argquery;        /* cursor arguments if any */
} PLpgSQL_stmt_forc;

/*
 * FOR statement running over EXECUTE
 */
typedef struct PLpgSQL_stmt_dynfors
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    PLpgSQL_rec *rec;
    PLpgSQL_row *row;
    List       *body;            /* List of statements */
    /* end of fields that must match PLpgSQL_stmt_forq */
    PLpgSQL_expr *query;
    List       *params;            /* USING expressions */
} PLpgSQL_stmt_dynfors;

/*
 * FOREACH item in array loop
 */
typedef struct PLpgSQL_stmt_foreach_a
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    char       *label;
    int            varno;            /* loop target variable */
    int            slice;            /* slice dimension, or 0 */
    PLpgSQL_expr *expr;            /* array expression */
    List       *body;            /* List of statements */
} PLpgSQL_stmt_foreach_a;

/*
 * OPEN a curvar
 */
typedef struct PLpgSQL_stmt_open
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    int            curvar;
    int            cursor_options;
    PLpgSQL_row *returntype;
    PLpgSQL_expr *argquery;
    PLpgSQL_expr *query;
    PLpgSQL_expr *dynquery;
    List       *params;            /* USING expressions */
} PLpgSQL_stmt_open;

/*
 * FETCH or MOVE statement
 */
typedef struct PLpgSQL_stmt_fetch
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_rec *rec;            /* target, as record or row */
    PLpgSQL_row *row;
    int            curvar;            /* cursor variable to fetch from */
    FetchDirection direction;    /* fetch direction */
    long        how_many;        /* count, if constant (expr is NULL) */
    PLpgSQL_expr *expr;            /* count, if expression */
    bool        is_move;        /* is this a fetch or move? */
    bool        returns_multiple_rows;    /* can return more than one row? */
} PLpgSQL_stmt_fetch;

/*
 * CLOSE curvar
 */
typedef struct PLpgSQL_stmt_close
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    int            curvar;
} PLpgSQL_stmt_close;

/*
 * EXIT or CONTINUE statement
 */
typedef struct PLpgSQL_stmt_exit
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    bool        is_exit;        /* Is this an exit or a continue? */
    char       *label;            /* NULL if it's an unlabelled EXIT/CONTINUE */
    PLpgSQL_expr *cond;
} PLpgSQL_stmt_exit;

/*
 * RETURN statement
 */
typedef struct PLpgSQL_stmt_return
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *expr;
    int            retvarno;
} PLpgSQL_stmt_return;

/*
 * RETURN NEXT statement
 */
typedef struct PLpgSQL_stmt_return_next
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *expr;
    int            retvarno;
} PLpgSQL_stmt_return_next;

/*
 * RETURN QUERY statement
 */
typedef struct PLpgSQL_stmt_return_query
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *query;        /* if static query */
    PLpgSQL_expr *dynquery;        /* if dynamic query (RETURN QUERY EXECUTE) */
    List       *params;            /* USING arguments for dynamic query */
} PLpgSQL_stmt_return_query;

/*
 * RAISE statement
 */
typedef struct PLpgSQL_stmt_raise
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    int            elog_level;
    char       *condname;        /* condition name, SQLSTATE, or NULL */
    char       *message;        /* old-style message format literal, or NULL */
    List       *params;            /* list of expressions for old-style message */
    List       *options;        /* list of PLpgSQL_raise_option */
} PLpgSQL_stmt_raise;

/*
 * RAISE statement option
 */
typedef struct PLpgSQL_raise_option
{
    PLpgSQL_raise_option_type opt_type;
    PLpgSQL_expr *expr;
} PLpgSQL_raise_option;

/*
 * ASSERT statement
 */
typedef struct PLpgSQL_stmt_assert
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *cond;
    PLpgSQL_expr *message;
} PLpgSQL_stmt_assert;

/*
 * Generic SQL statement to execute
 */
typedef struct PLpgSQL_stmt_execsql
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *sqlstmt;
    bool        mod_stmt;        /* is the stmt INSERT/UPDATE/DELETE?  Note:
                                 * mod_stmt is set when we plan the query */
    bool        into;            /* INTO supplied? */
    bool        strict;            /* INTO STRICT flag */
    PLpgSQL_rec *rec;            /* INTO target, if record */
    PLpgSQL_row *row;            /* INTO target, if row */
} PLpgSQL_stmt_execsql;

/*
 * Dynamic SQL string to execute
 */
typedef struct PLpgSQL_stmt_dynexecute
{
    PLpgSQL_stmt_type cmd_type;
    int            lineno;
    PLpgSQL_expr *query;        /* string expression */
    bool        into;            /* INTO supplied? */
    bool        strict;            /* INTO STRICT flag */
    PLpgSQL_rec *rec;            /* INTO target, if record */
    PLpgSQL_row *row;            /* INTO target, if row */
    List       *params;            /* USING expressions */
} PLpgSQL_stmt_dynexecute;

/*
 * Hash lookup key for functions
 */
typedef struct PLpgSQL_func_hashkey
{
    Oid            funcOid;

    bool        isTrigger;        /* true if called as a trigger */

    /* be careful that pad bytes in this struct get zeroed! */

    /*
     * For a trigger function, the OID of the trigger is part of the hash key
     * --- we want to compile the trigger function separately for each trigger
     * it is used with, in case the rowtype or transition table names are
     * different.  Zero if not called as a trigger.
     */
    Oid            trigOid;

    /*
     * We must include the input collation as part of the hash key too,
     * because we have to generate different plans (with different Param
     * collations) for different collation settings.
     */
    Oid            inputCollation;

    /*
     * We include actual argument types in the hash key to support polymorphic
     * PLpgSQL functions.  Be careful that extra positions are zeroed!
     */
    Oid            argtypes[FUNC_MAX_ARGS];
} PLpgSQL_func_hashkey;

/*
 * Trigger type
 */
typedef enum PLpgSQL_trigtype
{
    PLPGSQL_DML_TRIGGER,
    PLPGSQL_EVENT_TRIGGER,
    PLPGSQL_NOT_TRIGGER
} PLpgSQL_trigtype;

/*
 * Complete compiled function
 */
typedef struct PLpgSQL_function
{
    char       *fn_signature;
    Oid            fn_oid;
    TransactionId fn_xmin;
    ItemPointerData fn_tid;
    PLpgSQL_trigtype fn_is_trigger;
    Oid            fn_input_collation;
    PLpgSQL_func_hashkey *fn_hashkey;    /* back-link to hashtable key */
    MemoryContext fn_cxt;

    Oid            fn_rettype;
    int            fn_rettyplen;
    bool        fn_retbyval;
    bool        fn_retistuple;
    bool        fn_retset;
    bool        fn_readonly;

    int            fn_nargs;
    int            fn_argvarnos[FUNC_MAX_ARGS];
    int            out_param_varno;
    int            found_varno;
    int            new_varno;
    int            old_varno;
    int            tg_name_varno;
    int            tg_when_varno;
    int            tg_level_varno;
    int            tg_op_varno;
    int            tg_relid_varno;
    int            tg_relname_varno;
    int            tg_table_name_varno;
    int            tg_table_schema_varno;
    int            tg_nargs_varno;
    int            tg_argv_varno;

    /* for event triggers */
    int            tg_event_varno;
    int            tg_tag_varno;

    PLpgSQL_resolve_option resolve_option;

    bool        print_strict_params;

    /* extra checks */
    int            extra_warnings;
    int            extra_errors;

    /* the datums representing the function's local variables */
    int            ndatums;
    PLpgSQL_datum **datums;
    Bitmapset  *resettable_datums;    /* dnos of non-simple vars */

    /* function body parsetree */
    PLpgSQL_stmt_block *action;

    /* these fields change when the function is used */
    struct PLpgSQL_execstate *cur_estate;
    unsigned long use_count;
} PLpgSQL_function;

/*
 * Runtime execution data
 */
typedef struct PLpgSQL_execstate
{
    PLpgSQL_function *func;        /* function being executed */

    Datum        retval;
    bool        retisnull;
    Oid            rettype;        /* type of current retval */

    Oid            fn_rettype;        /* info about declared function rettype */
    bool        retistuple;
    bool        retisset;

    bool        readonly_func;

    TupleDesc    rettupdesc;
    char       *exitlabel;        /* the "target" label of the current EXIT or
                                 * CONTINUE stmt, if any */
    ErrorData  *cur_error;        /* current exception handler's error */

    Tuplestorestate *tuple_store;    /* SRFs accumulate results here */
    MemoryContext tuple_store_cxt;
    ResourceOwner tuple_store_owner;
    ReturnSetInfo *rsi;

    /* the datums representing the function's local variables */
    int            found_varno;
    int            ndatums;
    PLpgSQL_datum **datums;

    /* we pass datums[i] to the executor, when needed, in paramLI->params[i] */
    ParamListInfo paramLI;
    bool        params_dirty;    /* T if any resettable datum has been passed */

    /* EState to use for "simple" expression evaluation */
    EState       *simple_eval_estate;

    /* lookup table to use for executing type casts */
    HTAB       *cast_hash;
    MemoryContext cast_hash_context;

    /* memory context for statement-lifespan temporary values */
    MemoryContext stmt_mcontext;    /* current stmt context, or NULL if none */
    MemoryContext stmt_mcontext_parent; /* parent of current context */

    /* temporary state for results from evaluation of query or expr */
    SPITupleTable *eval_tuptable;
    uint64        eval_processed;
    Oid            eval_lastoid;
    ExprContext *eval_econtext; /* for executing simple expressions */

    /* status information for error context reporting */
    PLpgSQL_stmt *err_stmt;        /* current stmt */
    const char *err_text;        /* additional state info */

    void       *plugin_info;    /* reserved for use by optional plugin */
} PLpgSQL_execstate;

/*
 * A PLpgSQL_plugin structure represents an instrumentation plugin.
 * To instrument PL/pgSQL, a plugin library must access the rendezvous
 * variable "PLpgSQL_plugin" and set it to point to a PLpgSQL_plugin struct.
 * Typically the struct could just be static data in the plugin library.
 * We expect that a plugin would do this at library load time (_PG_init()).
 * It must also be careful to set the rendezvous variable back to NULL
 * if it is unloaded (_PG_fini()).
 *
 * This structure is basically a collection of function pointers --- at
 * various interesting points in pl_exec.c, we call these functions
 * (if the pointers are non-NULL) to give the plugin a chance to watch
 * what we are doing.
 *
 * func_setup is called when we start a function, before we've initialized
 * the local variables defined by the function.
 *
 * func_beg is called when we start a function, after we've initialized
 * the local variables.
 *
 * func_end is called at the end of a function.
 *
 * stmt_beg and stmt_end are called before and after (respectively) each
 * statement.
 *
 * Also, immediately before any call to func_setup, PL/pgSQL fills in the
 * error_callback and assign_expr fields with pointers to its own
 * plpgsql_exec_error_callback and exec_assign_expr functions.  This is
 * a somewhat ad-hoc expedient to simplify life for debugger plugins.
 */
typedef struct PLpgSQL_plugin
{
    /* Function pointers set up by the plugin */
    void        (*func_setup) (PLpgSQL_execstate *estate, PLpgSQL_function *func);
    void        (*func_beg) (PLpgSQL_execstate *estate, PLpgSQL_function *func);
    void        (*func_end) (PLpgSQL_execstate *estate, PLpgSQL_function *func);
    void        (*stmt_beg) (PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt);
    void        (*stmt_end) (PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt);

    /* Function pointers set by PL/pgSQL itself */
    void        (*error_callback) (void *arg);
    void        (*assign_expr) (PLpgSQL_execstate *estate, PLpgSQL_datum *target,
                                PLpgSQL_expr *expr);
} PLpgSQL_plugin;

/*
 * Struct types used during parsing
 */

typedef struct PLword
{
    char       *ident;            /* palloc'd converted identifier */
    bool        quoted;            /* Was it double-quoted? */
} PLword;

typedef struct PLcword
{
    List       *idents;            /* composite identifiers (list of String) */
} PLcword;

typedef struct PLwdatum
{
    PLpgSQL_datum *datum;        /* referenced variable */
    char       *ident;            /* valid if simple name */
    bool        quoted;
    List       *idents;            /* valid if composite name */
} PLwdatum;

/**********************************************************************
 * Global variable declarations
 **********************************************************************/

typedef enum
{
    IDENTIFIER_LOOKUP_NORMAL,    /* normal processing of var names */
    IDENTIFIER_LOOKUP_DECLARE,    /* In DECLARE --- don't look up names */
    IDENTIFIER_LOOKUP_EXPR        /* In SQL expression --- special case */
} IdentifierLookup;

extern IdentifierLookup plpgsql_IdentifierLookup;

extern int    plpgsql_variable_conflict;

extern bool plpgsql_print_strict_params;

extern bool plpgsql_check_asserts;

/* extra compile-time checks */
#define PLPGSQL_XCHECK_NONE            0
#define PLPGSQL_XCHECK_SHADOWVAR    1
#define PLPGSQL_XCHECK_ALL            ((int) ~0)

extern int    plpgsql_extra_warnings;
extern int    plpgsql_extra_errors;

extern bool plpgsql_check_syntax;
extern bool plpgsql_DumpExecTree;

extern PLpgSQL_stmt_block *plpgsql_parse_result;

extern int    plpgsql_nDatums;
extern PLpgSQL_datum **plpgsql_Datums;

extern char *plpgsql_error_funcname;

extern PLpgSQL_function *plpgsql_curr_compile;
extern MemoryContext plpgsql_compile_tmp_cxt;

extern PLpgSQL_plugin **plpgsql_plugin_ptr;

/**********************************************************************
 * Function declarations
 **********************************************************************/

/*
 * Functions in pl_comp.c
 */
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
                bool forValidator);
extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
extern void plpgsql_parser_setup(struct ParseState *pstate,
                     PLpgSQL_expr *expr);
extern bool plpgsql_parse_word(char *word1, const char *yytxt,
                   PLwdatum *wdatum, PLword *word);
extern bool plpgsql_parse_dblword(char *word1, char *word2,
                      PLwdatum *wdatum, PLcword *cword);
extern bool plpgsql_parse_tripword(char *word1, char *word2, char *word3,
                       PLwdatum *wdatum, PLcword *cword);
extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident);
extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents);
extern PLpgSQL_type *plpgsql_parse_wordrowtype(char *ident);
extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
                       Oid collation);
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
                       PLpgSQL_type *dtype,
                       bool add2namespace);
extern PLpgSQL_rec *plpgsql_build_record(const char *refname, int lineno,
                     bool add2namespace);
extern int plpgsql_recognize_err_condition(const char *condname,
                                bool allow_sqlstate);
extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname);
extern void plpgsql_adddatum(PLpgSQL_datum *new);
extern int    plpgsql_add_initdatums(int **varnos);
extern void plpgsql_HashTableInit(void);

/*
 * Functions in pl_handler.c
 */
extern void _PG_init(void);

/*
 * Functions in pl_exec.c
 */
extern Datum plpgsql_exec_function(PLpgSQL_function *func,
                      FunctionCallInfo fcinfo,
                      EState *simple_eval_estate);
extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func,
                     TriggerData *trigdata);
extern void plpgsql_exec_event_trigger(PLpgSQL_function *func,
                           EventTriggerData *trigdata);
extern void plpgsql_xact_cb(XactEvent event, void *arg);
extern void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
                   SubTransactionId parentSubid, void *arg);
extern Oid plpgsql_exec_get_datum_type(PLpgSQL_execstate *estate,
                            PLpgSQL_datum *datum);
extern void plpgsql_exec_get_datum_type_info(PLpgSQL_execstate *estate,
                                 PLpgSQL_datum *datum,
                                 Oid *typeid, int32 *typmod, Oid *collation);

/*
 * Functions for namespace handling in pl_funcs.c
 */
extern void plpgsql_ns_init(void);
extern void plpgsql_ns_push(const char *label,
                PLpgSQL_label_type label_type);
extern void plpgsql_ns_pop(void);
extern PLpgSQL_nsitem *plpgsql_ns_top(void);
extern void plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
                  const char *name1, const char *name2,
                  const char *name3, int *names_used);
extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,
                        const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur);

/*
 * Other functions in pl_funcs.c
 */
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt);
extern const char *plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind);
extern void plpgsql_free_function_memory(PLpgSQL_function *func);
extern void plpgsql_dumptree(PLpgSQL_function *func);

/*
 * Scanner functions in pl_scanner.c
 */
extern int    plpgsql_base_yylex(void);
extern int    plpgsql_yylex(void);
extern void plpgsql_push_back_token(int token);
extern bool plpgsql_token_is_unreserved_keyword(int token);
extern void plpgsql_append_source_text(StringInfo buf,
                           int startlocation, int endlocation);
extern int    plpgsql_peek(void);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
              int *tok2_loc);
extern int    plpgsql_scanner_errposition(int location);
extern void plpgsql_yyerror(const char *message) pg_attribute_noreturn();
extern int    plpgsql_location_to_lineno(int location);
extern int    plpgsql_latest_lineno(void);
extern void plpgsql_scanner_init(const char *str);
extern void plpgsql_scanner_finish(void);

/*
 * Externs in gram.y
 */
extern int    plpgsql_yyparse(void);

#endif                            /* PLPGSQL_H */
